
/**
 * by gsunwu@foxmail.com
 * 转换的类文件
 */
/**
 * 封装格式
 * <p>
 * （以太网层）原地址（MAC），目的地址（MAC），类型（v4 or v6）+
 * （IP层）版本（version4 or version6），区分服务，总长度，
 * TCP成
 */


/*
 * 文件内容：MAC帧封装
 * 文件目标：输入一段不超过1500个字节的数据，输出封装后的MAC帧，
 *        MAC帧包含目的地址、源地址、长度/类型、数据，FCS 共5部分
 *        各部分有单独输出显示，输出为2进制和16进制
 * 时间：2018-11-15
 * 作者：wowpH
 */

import java.util.Scanner;

public class Trans {


    private int LENGTH_FIELD_BYTES = 2;       // 长度字段长度为2 bytes
    private int MIN_DATA_BYTES_LENGTH = 46;   // 数据字段最小长度46 	bytes
    private int MIN_DATA_BITS_LENGTH = 368;   // 数据字段最小长度368 bits
    private int MAX_DATA_BYTES_LENGTH = 1500; // 数据字段最大长度1500 bytes
    private int FCS_BITS_LENGTH = 32;         // FCS长度（bit）
    private String DESTINATION_ADDRESS = "3c8c409d93a7";            // 目的地址
    private String SOURCE_ADDRESS = "1866da0740ee";                 // 源地址
    private String DIVISOR_P = "100000100110000010001110110110111"; // CRC检验的除数p
    private String destinationBinaryAddress = ""; // 目的地址二进制
    private String sourceBinaryAddress = "";      // 源地址二进制
    private String lengthField = "";   // 长度字段2进制，2 bytes
    private String data = "";          // 输入的数据部分
    private String dataBinary = "";    // 输入的数据部分的2进制
    private String fcs = "";           // CRC检验的FCS二进制
    private String mac = "";           // MAC帧，包含目的地址、源地址、长度字段、数据字段，FCS共5部分
    private String macHex = "";        // MAC帧16进制串
    private String fcsHex = "";        // FCS十六进制串
    // MAC帧初始长度（byte）为目的地址长度（6bytes）+源地址长度（6bytes）+长度字段（2bytes）+FCS长度（4bytes）
    private int macByteLength = 18;
    // 4位的二进制数串，0-15
    String[] FOUR_BIT_BINARY_TABLE = {
            "0000", "0001", "0010", "0011",
            "0100", "0101", "0110", "0111",
            "1000", "1001", "1010", "1011",
            "1100", "1101", "1110", "1111"
    };


    public String getMac() {
        return this.mac;
    }

    // 输入数据
    private void input() {
        Scanner in = new Scanner(System.in);
        String data = "";
        while (data.equals("")) {
            System.out.println("请输入传输的数据：");
            data = in.nextLine();
            this.data = data;
        }
        System.out.println("---------------------------------------------------------");
        in.close();
    }

    // 显示信息
    void output() {
        System.out.println("MAC帧（2进制）为：");
        System.out.println(this.mac);
        System.out.println("目的地址为：");
        System.out.println(this.destinationBinaryAddress);
        System.out.println("源地址为：");
        System.out.println(this.sourceBinaryAddress);
        System.out.println("长度/类型（2进制）为：");
        System.out.println(this.lengthField);
        System.out.println("数据为(二进制)：");
        System.out.println(this.dataBinary);
        System.out.println("数据为（十六进制）：");
        System.out.println(this.binaryToHex(this.dataBinary));
        System.out.println("FCS（2进制）为：");
        System.out.println(this.fcs);
        System.out.println("MAC帧（16进制）为：");
        System.out.println(this.macHex);
        System.out.println("FCS（16进制）为：");
        System.out.println(this.fcsHex);
        System.out.println("---------------------------------------------------------");
    }

    public  void setStr(String str){
        this.data = str;
        this.mac = "";
        this.dealWithDestinationAddress();
        this.dealWithSourceAddress();
        this.dealWithData();
        this.dealWithLengthField();
        this.mac += this.lengthField; // 将长度字段添加到MAC帧
        this.mac += this.dataBinary;  // 将数据字段添加到MAC帧
        this.dealWithFcs();
        this.dealWithMac();
    }
    // 处理目的地址
    private void dealWithDestinationAddress() {
        this.destinationBinaryAddress = this.hexToBinary(this.DESTINATION_ADDRESS);
        this.mac += this.destinationBinaryAddress;        // 将目的地址（2进制）添加到MAC帧
    }

    // 处理源地址
    private void dealWithSourceAddress() {
        this.sourceBinaryAddress = this.hexToBinary(this.SOURCE_ADDRESS);
        this.mac += this.sourceBinaryAddress;        // 将源地址（2进制）添加到MAC帧
    }

    // 处理传输数据
    public String dealWithData() {
        int dataByteLength = 0;    // 数据长度（byte）
        this.dataBinary = this.dataToBinaryString(data);
        dataByteLength = this.dataBinary.length() / 8;
        if (dataByteLength < this.MIN_DATA_BYTES_LENGTH) {
            this.dataBinary = this.fillZero(this.dataBinary, this.MIN_DATA_BITS_LENGTH, false);
            dataByteLength = this.MIN_DATA_BYTES_LENGTH;
        } else if (dataByteLength > this.MAX_DATA_BYTES_LENGTH) {
            System.out.println("输入的数据超过范围！");
            System.exit(0);
        }
        this.macByteLength += dataByteLength;
        return this.dataBinary;
    }



    // 处理长度字段
    private void dealWithLengthField() {
        this.lengthField = Integer.toBinaryString(this.macByteLength);
        this.lengthField = this.fillZero(this.lengthField, this.LENGTH_FIELD_BYTES * 8, true);
    }

    // 处理FCS
    private void dealWithFcs() {
        String dividend = this.fillZero(this.mac, this.mac.length() + this.FCS_BITS_LENGTH, false);
        this.fcs = this.solveFcs(dividend, this.DIVISOR_P);
        this.fcsHex = this.binaryToHex(this.fcs);
        this.mac += this.fcs;
    }

    // 处理MAC帧
    private void dealWithMac() {
        this.macHex = this.binaryToHex(this.mac);
    }

    /* @return String 返回求解到的FCS串
     * @param dividend 被除数，二进制
     * @param divisor 除数，二进制
     */
    private String solveFcs(String dividend, String divisor) {
        String fcs = "";
        char[] dividendArray = dividend.toCharArray();
        char[] divisorArray = divisor.toCharArray();
        // 指向被除数中用于当前运算的数据的首部和尾部，长度和除数相同
        int head = 0;     // 指向头部下标，第一次是0
        int tail = head + divisor.length() - 1;  // 尾部下标，第一次是32
        int i;
        while (tail < dividend.length()) {
            if ('1' == dividendArray[head]) {
                for (i = head; i <= tail; ++i) {
                    if (dividendArray[i] == divisorArray[i - head]) {
                        dividendArray[i] = '0';
                    } else {
                        dividendArray[i] = '1';
                    }
                }
            }
            ++head;
            ++tail;
        }
        for (i = head; i < dividendArray.length; ++i) {
            fcs += dividendArray[i];
        }
        return fcs;
    }

    /* @return String 二进制字符串
     * @param data 字符串
     * 将字符串转换成二进制字符串，通过ASCII转换
     */
    private String dataToBinaryString(String data) {
        char[] stringArray = data.toCharArray();
        String result = "";
        String oneBit = "";
        for (int i = 0; i < stringArray.length; ++i) {
            oneBit = Integer.toBinaryString(stringArray[i]);
            oneBit = this.fillZero(oneBit, 8, true);
            result += oneBit;
        }
        return result;
    }

    /* @return String 填充0之后的二进制串
     * @param data 需要填充0的二进制串
     * @param finalBitLength 填充0后的长度（位）
     * @param way 填充0。way=true 首部填充0，way=false 尾部填充0
     */
    private String fillZero(String data, int finalBitLength, boolean way) {
        String result = "";
        for (int i = data.length(); i < finalBitLength; ++i) {
            result += '0';
        }
        if (true == way) {
            result += data;
        } else {
            result = data + result;
        }
        return result;
    }

    /* @return String 返回转换后的二进制串
     * @param hex 要转换的十六进制串
     */
    private String hexToBinary(String hex) {
        String binary = "";
        int hexBitLength = hex.length();
        for (int i = 0; i < hexBitLength; ++i) {
            if (hex.charAt(i) >= '0' && hex.charAt(i) <= '9') {
                binary += this.FOUR_BIT_BINARY_TABLE[hex.charAt(i) - '0'];
            } else if (hex.charAt(i) >= 'a' && hex.charAt(i) <= 'z') {
                binary += this.FOUR_BIT_BINARY_TABLE[hex.charAt(i) - 'a' + 10];
            } else if (hex.charAt(i) >= 'A' && hex.charAt(i) <= 'Z') {
                binary += this.FOUR_BIT_BINARY_TABLE[hex.charAt(i) - 'A' + 10];
            }
        }
        return binary;
    }

    /* @return String 16进制串
     * @param binary 需要转换成16进制的2进制串
     */
    private String binaryToHex(String binary) {
        String hex = "";
        String fourBits = "";
        int oneBit = 0;
        int length = binary.length() / 4;
        for (int i = 0; i < length; ++i) {
            fourBits = binary.substring(0, 4);
            oneBit = Integer.parseInt(fourBits, 2);
            hex += Integer.toHexString(oneBit);
            binary = binary.substring(4);
        }
        return hex;
    }

}